/*
	
2. 通过平台总线实现点灯效果									

*/


#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h> 

#include "led.h"

//定义led的设备结构体(类)
struct rk_led {
	unsigned int major;
    struct class *cls;
    struct device *dev;
	unsigned int gpio_led; //保存引脚号
	int value; //保存led_write接收数据
};


static struct rk_led *led_device;



//交给驱动框架来调用的回调函数
static int led_open (struct inode *inode, struct file *filp)
{
	gpio_direction_output(led_device->gpio_led, 1); //led on
	printk("<kernel>call %s()\n", __func__); 
	return 0;
}

static int led_close (struct inode *inode, struct file *filp)
{
	gpio_direction_output(led_device->gpio_led, 0); //led off
	printk("<kernel>call %s()\n", __func__); 
	return 0;

}


ssize_t led_read(struct file *filp, char __user *buf, size_t size, loff_t *flag)
{
	printk("<kernel>call %s()\n", __func__); 
	return 0;

}


ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{

	printk("<kernel>call %s()\n", __func__); 
	return 0;


}


static struct file_operations fops = { //只部分初始化自己用的
	.owner = THIS_MODULE, //当前模块绑定
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = led_close,

};






//当平台驱动和平台设备匹配时调用probe函数
static int led_probe(struct platform_device *pdev)
{
	int ret;
	//pdev对象就是匹配成功的平台设备对象	
	struct leds_platform_data *pdata = NULL;
	pdata = (struct leds_platform_data *)pdev->dev.platform_data;
	
	if (!pdata) {
		printk("<kernel> leds_platform_data get fail\n");
		return -EINVAL;
	}
	
	//创建led设备对象

	led_device = kmalloc(sizeof(struct rk_led), GFP_KERNEL);
	if (!led_device) {
		printk("kmalloc fail\n");
		return -ENOMEM;
	}
	

	//动态申请设备号
	led_device->major = register_chrdev(0, "led_drv", &fops);
	if (led_device->major < 0) {
		printk("<kernel>register_chrdev fail\n");
		ret = -EBUSY;
		goto err_register_chrdev;

	}
	printk("<kernel> major=%d\n", led_device->major);


	//创建设备类

	led_device->cls = class_create(THIS_MODULE, "led_cls");
	if (IS_ERR(led_device->cls)) {			
		printk("couldn't create class\n");	
		
		ret = PTR_ERR(led_device->cls);	
		goto err_class_create;
	}			
	

	//创建设备对象==创建设备文件
	led_device->dev = device_create(led_device->cls, NULL, MKDEV(led_device->major, 0), NULL,  "led"); // /dev/led
	if (IS_ERR(led_device->dev)) { 											
		printk("device_create fail\n");
		ret = PTR_ERR(led_device->dev); 	
		goto err_device_create;
	}


	//硬件初始化
	led_device->gpio_led = pdata->leds[0].gpio; //GPIO1_C7   计算引脚号	55 	

	//申请引脚
	ret = gpio_request(led_device->gpio_led, pdata->leds[0].desc);
	if (ret) {
		printk("gpio_request fail\n");
		goto err_gpio_request;

	}
	//设置引脚为输出功能,默认低电平
	gpio_direction_output(led_device->gpio_led, 0);

	
	
	printk("<kernel>call %s()\n", __func__);  
	return 0;

//集中错误处理
err_gpio_request:
	device_destroy(led_device->cls, MKDEV(led_device->major, 0));
err_device_create:
	class_destroy(led_device->cls);
err_class_create:
	unregister_chrdev(led_device->major, "led_drv");
err_register_chrdev:
	kfree(led_device);
	return ret;
	
}

//当平台驱动和平台设备分离时调用remove函数
static int led_remove(struct platform_device *pdev)
{
	//释放引脚
	gpio_free(led_device->gpio_led);
	device_destroy(led_device->cls, MKDEV(led_device->major, 0));
	class_destroy(led_device->cls);
	unregister_chrdev(led_device->major, "led_drv");
	kfree(led_device);
	printk("<kernel>call %s()for oop @ %d\n", __FUNCTION__, __LINE__); 

	return 0;

}

static const struct platform_device_id led_id_table[] = {
	{
		.name = "rk3399_led",
		.driver_data = 0x1,
	}, {
		.name = "rk3288_led",
		.driver_data = 0x2,
	}, {
		.name = "rk3568_led",
		.driver_data = 0x3,
	}, {
		/* id table */
	}
};



//定义平台驱动对象并初始化
static struct platform_driver rk_led_driver = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "rk_led", //平台驱动的字符串名称,也是匹配的条件
	},
	.id_table = led_id_table, //指向匹配平台设备的数组
};



static int __init driver_mod_init(void)
{
	printk("<kernel> call %s()\n", __func__);  
	return platform_driver_register(&rk_led_driver);
}


static void __exit driver_mod_exit(void)
{
	printk("<kernel> call %s() @ %d\n", __FUNCTION__, __LINE__);  
	platform_driver_unregister(&rk_led_driver);
}


module_init(driver_mod_init); 
module_exit(driver_mod_exit); 
MODULE_LICENSE("GPL"); 



